﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml;
using System.Xml.Linq;

using Perseus;

namespace Hetymine.Executioner.Data {
    public class HistoryManager : IEnumerable<string> {
        private List<HistoryItem> _History { get; set; }

        public HistoryManager() {
            // Check for old history.txt
            this.FileName = Executioner.Directory + "history.txt";
            if (File.Exists(this.FileName)) {
                this.Load(this.FileName);
                File.Delete(this.FileName); // Delete old history file
                this.FileName = Executioner.Directory + "history.xml";
                this.Save(); // Save new history
            }
            else {
                this.FileName = Executioner.Directory + "history.xml";
                this.Load(this.FileName);
            }
        }
        public HistoryManager(string fileName) {
            this.FileName = fileName;
            this.Load(this.FileName);
        }

        public string this[int index] {
            get {
                index = index.MaxMin(0, this._History.Count - 1);
                return this._History[index].Command;
            }
        }

        public int Count { get { return this._History.Count; } }

        public void Load() {
            this.Load(this.FileName);
        }
        public void Load(string fileName) {
            this._History = new List<HistoryItem>();

            if (!File.Exists(fileName)) {
                return;
            }

            if (Path.GetExtension(fileName) == ".txt") {
                string[] items = File.ReadAllLines(fileName);
                foreach (string s in items) {
                    this._History.Add(new HistoryItem(s));
                }
            }
            else {
                string data = Executioner.Data.Load(fileName);

                XDocument xmlDoc = XDocument.Parse(data);

                if (xmlDoc.Element("History") == null) {
                    return;
                }

                var historyItems = from h in xmlDoc.Element("History").Elements("Item")
                                   select h;

                foreach (var h in historyItems) {
                    HistoryItem historyItem = new HistoryItem();
                    historyItem.Load(h);
                    this._History.Add(historyItem);
                }
            }
        }
        public void Save() {
            this.Save(this.FileName);
        }
        public void Save(string fileName) {
            if (Executioner.Config.GetBool("Executioner", "Save History", true) && this._History.Count > 0) {
                XmlWriterSettings xws = new XmlWriterSettings();
                xws.Encoding = System.Text.Encoding.UTF8;
                xws.Indent = true;
                xws.IndentChars = "    ";
                xws.NewLineChars = Environment.NewLine;
                xws.NewLineHandling = NewLineHandling.Replace;
                xws.OmitXmlDeclaration = true;

                string data;
                using (var sw = new StringWriter()) {
                    XmlWriter xw = XmlWriter.Create(sw, xws);

                    XElement xe = new XElement("History");
                    xe.Add(new XAttribute("Version", "1.0"));

                    // Add command xelements                
                    foreach (HistoryItem h in this._History) {
                        XElement hxe = h.Save();
                        if (hxe != null) {
                            xe.Add(hxe);
                        }
                    }

                    xe.WriteTo(xw);

                    xw.Close();

                    data = sw.ToString();
                }

                Executioner.Data.Save(fileName, data);
            }
            else if (File.Exists(fileName)) {
                File.Delete(fileName);
            }
        }

        public string FileName { get; set; }

        public void Add(string command) {
            int maxHistory = Executioner.Config.GetInt("Executioner", "Max History Items", 250);
            // Do not add exit or clear to history
            if ((maxHistory == -1 || maxHistory > 0) &&
                command != "-clear" &&
                !command.StartsWith("-clear ", StringComparison.Ordinal) &&
                command != "-exit"                
            ) {
                int index = this.IndexOf(command);
                if (index >= 0) {
                    // Remove history item at it's current index and insert it back at index 0
                    HistoryItem tmp = this._History[index];
                    this._History.RemoveAt(index);
                    tmp.ExecutionDates.Add(DateTime.Now);
                    this._History.Insert(0, tmp);
                }
                else {
                    HistoryItem tmp = new HistoryItem(command);
                    this._History.Insert(0, tmp);
                }
                
                if (maxHistory > 0 && this._History.Count > maxHistory) {
                    this._History.RemoveRange(maxHistory, this._History.Count - maxHistory);
                }
            }
        }
        public bool Remove(string command) {
            for (int i = this._History.Count - 1; i > 0; --i) {
                if (this._History[i].Command == command) {
                    this._History.RemoveAt(i);
                    return true;
                }
            }

            return false;
        }
        public bool Remove(int index) {
            if (index >= 0 && index < this._History.Count) {
                this._History.RemoveAt(index);
                return true;
            }

            return false;            
        }

        public bool Contains(string command) {
            foreach (HistoryItem h in this._History) {
                if (h.Command == command) {
                    return true;
                }
            }

            return false;
        }

        public int IndexOf(string command) {
            for (int i = 0; i < this._History.Count; ++i) {
                if (this._History[i].Command == command) {
                    return i;
                }
            }

            return -1;
        }

        public void Clear(string parameters) {
            if (parameters.IsEmpty()) {
                this._History.Clear();
            }
            else {
                List<HistoryItem> newHistory = new List<HistoryItem>();
                foreach (HistoryItem h in this._History) {
                    if (h.Command == parameters || h.Command.StartsWith(parameters + " ", StringComparison.Ordinal)) {
                        continue;
                    }
                    newHistory.Add(h);
                }
                this._History = newHistory;
            }
        }

        #region IEnumerator
        public IEnumerator<string> GetEnumerator() {
            foreach (HistoryItem h in this._History) {
                yield return h.Command;
            }
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return (IEnumerator)this.GetEnumerator();   
        }
        #endregion
    }
}
